package com.pig4cloud.pigx.common.core.util.wechat;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.pig4cloud.pigx.common.core.util.R;
import io.micrometer.core.instrument.util.StringUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.springframework.data.redis.core.RedisTemplate;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

@Slf4j
@AllArgsConstructor
public class WechatUtils {

	// 蒙牛参数
	// private final static String YOURUI_USER_APPID = "wx476fee11d5ee00eb";

	// private final static String YOURUI_USER_APPSECRET =
	// "e78310102c5e72d52039d552262cd404";

	private final static String YOURUI_USER_APPID = "wx476fee11d5ee00eb";

	private final static String YOURUI_USER_APPSECRET = "f5bdaf0418a3e39c17cc355445c812d1";

	private final RedisTemplate redisTemplate;

	/**
	 * 获取openId
	 * @param code
	 * @return
	 */
	public static R authorize(String code) {
		GetOpenIdUtil wechat = new GetOpenIdUtil();
		R result = null;
		result = wechat.getWechatOpenId(YOURUI_USER_APPID, code, YOURUI_USER_APPSECRET);

		// 成功返回的场合
		if (result.getCode() == 0) {
			JSONObject jsonObject = JSON.parseObject(result.getData().toString());
			String openId = jsonObject.getString("openid");
			// String sessionKey = jsonObject.getString("session_key");
			log.info("微信获取openId的返回值: " + JSON.toJSONString(jsonObject));
			// Map<String, String> maps = new HashMap<>();
			// maps.put("openId", openId);
			// maps.put("sessionKey", sessionKey);

			if (!StringUtils.isBlank(openId)) {
				return R.ok(JSON.toJSONString(jsonObject));
			}
			else {
				// 授权失败
				return R.failed("授权失败, 请重新授权");
			}
		}
		else {
			// 授权失败
			return R.failed("授权失败, 请重新授权");
		}
	}

	/**
	 * 小程序支付
	 * @param orderNo
	 * @param money
	 * @param openId
	 * @return
	 * @throws Exception
	 */
	public static Map<String, String> xcxPayment(String orderNo, Integer money, String openId) throws Exception {
		log.info("【小程序支付】 统一下单开始, 订单编号=" + orderNo);
		SortedMap<String, String> resultMap = new TreeMap<String, String>();
		// 生成支付金额，开发环境处理支付金额数到0.01、0.02、0.03元
		// double payAmount = 0.01;
		// double payAmount = money / 100;
		Integer payAmount = money;
		// 添加或更新支付记录(参数跟进自己业务需求添加)
		Map<String, String> resMap = xcxUnifieldOrder(orderNo, PayConfig.TRADE_TYPE_JSAPI, payAmount, openId);
		if ("SUCCESS".equals(resMap.get("return_code"))) {
			String sign = "";
			resultMap.put("appId", YOURUI_USER_APPID);
			sign = PayConfig.YOURUI_XCX_KEY;
			// resultMap.put("appId", PayConfig.XCX_APP_ID);
			resultMap.put("timeStamp", PayUtil.getCurrentTimeStamp());
			resultMap.put("nonceStr", PayUtil.makeUUID(32));
			resultMap.put("package", "prepay_id=" + resMap.get("prepay_id"));
			resultMap.put("signType", "MD5");
			resultMap.put("sign", PayUtil.createSign(resultMap, sign));
			resultMap.put("returnCode", "SUCCESS");
			resultMap.put("returnMsg", "OK");
			log.info("【小程序支付】统一下单成功，返回参数:" + resultMap);
		}
		else {
			resultMap.put("returnCode", resMap.get("return_code"));
			resultMap.put("returnMsg", resMap.get("return_msg"));
			log.info("【小程序支付】统一下单失败，失败原因:" + resMap.get("return_msg"));
		}
		return resultMap;
	}

	/**
	 * 小程序支付统一下单
	 */
	private static Map<String, String> xcxUnifieldOrder(String orderNum, String tradeType, Integer payAmount,
			String openid) throws Exception {
		// 封装参数
		String sign = "";
		SortedMap<String, String> paramMap = new TreeMap<String, String>();
		paramMap.put("appid", YOURUI_USER_APPID);
		paramMap.put("mch_id", PayConfig.YOURUI_XCX_MCH_ID);
		sign = PayConfig.YOURUI_XCX_KEY;
		// paramMap.put("appid", PayConfig.XCX_APP_ID);

		paramMap.put("nonce_str", PayUtil.makeUUID(32));
		paramMap.put("body", "伊利高个子奶粉套装");
		paramMap.put("out_trade_no", orderNum);
		// paramMap.put("total_fee", PayUtil.moneyToIntegerStr(payAmount));
		paramMap.put("total_fee", payAmount.toString());
		paramMap.put("spbill_create_ip", PayUtil.getLocalIp());
		paramMap.put("notify_url", getNotifyUrl());
		paramMap.put("trade_type", tradeType);
		paramMap.put("openid", openid);
		paramMap.put("sign", PayUtil.createSign(paramMap, sign));
		// 转换为xml
		String xmlData = PayUtil.mapToXml(paramMap);
		// 请求微信后台，获取预支付ID
		String resXml = HttpUtil.post(PayConfig.WX_PAY_UNIFIED_ORDER, xmlData);
		log.info("【小程序支付】 统一下单响应：\n" + resXml);
		return PayUtil.xmlStrToMap(resXml);
	}

	/**
	 * 申请退款
	 * @param orderId 商户订单号
	 * @param refundId 商户退款单号
	 * @param totalFee 订单金额
	 * @param refundFee 退款金额
	 * @param refundAccount 退款资金来源（默认传 "REFUND_SOURCE_UNSETTLED_FUNDS"） 注: 退款金额不能大于订单金额
	 */
	public static Map<String, String> xcxRefund(String orderId, String refundId, Integer totalFee, Integer refundFee,
			String refundAccount) throws Exception {
		log.info("退款金额:" + totalFee);
		SortedMap<String, String> paramMap = new TreeMap<String, String>();
		paramMap.put("appid", YOURUI_USER_APPID);
		paramMap.put("mch_id", PayConfig.YOURUI_XCX_MCH_ID);
		paramMap.put("nonce_str", PayUtil.makeUUID(32));
		paramMap.put("out_trade_no", orderId); // 商户订单号和微信订单号二选一(我这里选的是商户订单号)
		paramMap.put("out_refund_no", refundId);
		// double actualTotalFee = totalFee / 100;
		paramMap.put("total_fee", totalFee.toString());
		// double actualRefundFee = refundFee / 100;
		paramMap.put("refund_fee", refundFee.toString());
		paramMap.put("refund_account", refundAccount);
		paramMap.put("sign_type", "MD5");
		paramMap.put("sign", PayUtil.createSign(paramMap, PayConfig.YOURUI_XCX_KEY));
		String xmlData = PayUtil.mapToXml(paramMap);
		// 请求微信后台，获取预支付ID
		// String resXml = HttpUtil.post(PayConfig.WX_REFUND, xmlData);
		String resXml = payOfCertificate(PayConfig.WX_REFUND, xmlData,
				"/usr/local/nginx/cert/wechat/apiclient_cert.p12", PayConfig.YOURUI_XCX_MCH_ID);
		log.info("【小程序支付】 退款响应：\n" + resXml);
		Map<String, String> resMap = PayUtil.xmlStrToMap(resXml);
		SortedMap<String, String> resultMap = new TreeMap<String, String>();

		if ("SUCCESS".equals(resMap.get("return_code"))) {
			if ("SUCCESS".equals(resMap.get("result_code"))) {
				resultMap.put("returnCode", "SUCCESS");
				resultMap.put("returnMsg", "OK");
				log.info("【小程序支付】退款成功" + orderId.toString());
			}
			else {
				resultMap.put("returnCode", resMap.get("err_code"));
				resultMap.put("returnMsg", resMap.get("err_code_des"));
				log.info("【小程序支付】退款失败，失败原因:" + resMap.get("err_code_des"));
			}
		}
		else {
			resultMap.put("returnCode", resMap.get("return_code"));
			resultMap.put("returnMsg", resMap.get("return_msg"));
			log.info("【小程序支付】退款失败，失败原因:" + resMap.get("err_code_des"));
		}

		return resultMap;
	}

	/**
	 * @Description: 加载证书 发送请求
	 * @Author: Mr.Jkx
	 * @date: 2020/3/23 16:06 url：退款请求地址 data：请求数据 certificatepath：证书路径 mch_id：商品平台mch_id
	 */
	public static String payOfCertificate(String url, String data, String certificatepath, String mch_id)
			throws Exception {
		KeyStore keyStore = KeyStore.getInstance("PKCS12");
		FileInputStream is = new FileInputStream(new File(certificatepath));
		try {
			keyStore.load(is, mch_id.toCharArray());
		}
		finally {
			is.close();
		}
		SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mch_id.toCharArray()).build();
		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,
				// new String[] { "TLSv1" },
				null, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
		CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
		try {
			HttpPost httPost = new HttpPost(url); // 设置响应头信息
			httPost.addHeader("Connection", "keep-alive");
			httPost.addHeader("Accept", "*/*");
			httPost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
			httPost.addHeader("Host", "api.mch.weixin.qq.com");
			httPost.addHeader("X-Requested-With", "XMLHttpRequest");
			httPost.addHeader("Cache-Control", "max-age=0");
			httPost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
			httPost.setEntity(new StringEntity(data, "UTF-8"));
			CloseableHttpResponse response = httpclient.execute(httPost);
			try {
				HttpEntity entity = response.getEntity();

				String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
				EntityUtils.consume(entity);
				return jsonStr;
			}
			finally {
				response.close();
			}
		}
		finally {
			httpclient.close();
		}
	}

	public static String getPhone(String code) {

		// 获取accessToken
		String accessToken = getAccessToken();
		String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken;
		Map<String, String> paramMap = new HashMap<>();
		paramMap.put("code", code);
		String post = HttpUtil.post(url, JSON.toJSONString(paramMap));
		JSONObject jsonObject = JSON.parseObject(post);

		String errCode = jsonObject.getString("errcode");
		if (errCode.equals("0")) {
			String phoneInfo = jsonObject.getString("phone_info");
			JSONObject jsonObject1 = JSONObject.parseObject(phoneInfo);
			String phone = jsonObject1.getString("phoneNumber");
			return phone;
		}

		return "";
	}

	/**
	 * 获取ticket
	 * @param accessToken
	 * @return
	 */
	private String getJsapiTicket(String accessToken) {
		String jsapiTicketResp = HttpUtil
				.get("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + accessToken + "&type=jsapi");
		log.info("调用微信的接口 获取jsapi_ticket，响应数据为:\n{}", jsapiTicketResp);
		String ticket = JSON.parseObject(jsapiTicketResp).getString("ticket");
		redisTemplate.opsForValue().set("ggz_wechat_ticket", ticket, Duration.ofSeconds(7000));
		log.info("写入缓存ticket");
		return ticket;
	}

	/**
	 * 获取accessToken
	 * @return
	 */
	public static String getAccessToken() {
		String accessTokenResp = HttpUtil
				.get("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + YOURUI_USER_APPID
						+ "&secret=" + YOURUI_USER_APPSECRET + "");
		log.info("调用微信的接口 获取AccessToken，响应数据为:\n{}", accessTokenResp);
		return JSON.parseObject(accessTokenResp).getString("access_token");
	}

	private static String getNotifyUrl() {
		// 服务域名
		// return PayConfig.PRO_SERVER_DOMAIN + "/third/wechat/xcxNotify";
		return PayConfig.PRO_SERVER_DOMAIN + "/shop_order/order/wechat/pay/callback";
	}

}
